﻿using System;
using System.Collections.Generic;
using UnityEngine;

public class PathSeeker : BaseMono {
	/// <summary>
	/// 节点寻找方式
	/// </summary>
	public PathDir pathDir = PathDir.FourDir;

	/// <summary>
	/// 寻找到路径回调（找到路径后执行此函数，注1：不管有没有找到最短路径；注2：对外不公开Node，防止死循环）
	/// </summary>
	public event Action<List<Vector3>> OnFindPath;

	/// <summary>
	/// 节点测试
	/// </summary>
	public event Action<int, DrawNodeType, AStarNode> OnDrawTest;

	/// <summary>
	/// 一次查找完成
	/// </summary>
	private bool isFindPath = false;

	private Vector3 startPos;
	private Vector3 endPos;

	public override void onStart()
	{
		base.onStart();
		OnFindPath += o => AStarGridMgr.grid.ClearDirty();
		OnDrawTest += AStarRes.DrawNode;
		base.Init();
	}

	public override void onUpdate()
	{
		base.onUpdate();
		FindPath(startPos, endPos);
	}

	/// <summary>
	/// 设置初始位置和目标位置
	/// </summary>
	/// <param name="start"></param>
	/// <param name="end"></param>
	public void SetPosition(Vector3 start, Vector3 end)
	{
		IntVec2 vec1 = new IntVec2((int)endPos.x, (int)endPos.y);
		IntVec2 vec2 = new IntVec2((int)end.x, (int)end.y);

		if (AStarGridMgr.grid.IsDirty || vec1 != vec2) {
			startPos = start;
			endPos = end;
			ReCalculatePath();
		}
	}

	private void ReCalculatePath()
	{
		isFindPath = false;
	}

	/// <summary>
	/// 路径寻找
	/// </summary>
	/// <param name="start"></param>
	/// <param name="end"></param>
	public void FindPath(Vector3 start, Vector3 end) 
	{
		if (isFindPath) {
			return;
		}

		AStarNode startNode = AStarGridMgr.grid.GetNodeByWorldPos(startPos);
		AStarNode endNode = AStarGridMgr.grid.GetNodeByWorldPos(endPos);
		
		if (startNode == null || endNode == null) {
			MyLog.LogError("Node Out Grid ! ");
			return;
		}

		List<AStarNode> openList = new List<AStarNode>();
		List<AStarNode> closeList = new List<AStarNode>();
		AStarNode curNode = null;
		openList.Add(startNode);

		int step = 0;

		while (openList.Count > 0) {
			step++;
			curNode = openList[0];

			for (int i = 0, length = openList.Count; i < length; i++) {
				AStarNode tempNode = openList[i];

				if (tempNode.FCost <= curNode.FCost) {
					//优先寻找离起始点相近
					if (tempNode.GCost < curNode.GCost) {
						curNode = tempNode;
					}
						//且靠近目标点的节点
					else if (tempNode.HCost <= curNode.HCost) {
						curNode = tempNode;
					}
				}
			}

			if (curNode == endNode) {
				isFindPath = true;
				MyLog.LogError("Find Path Successfully!");
				if (OnFindPath != null)
				{
					OnFindPath(GetPath(startNode, curNode));
				}
				return;
			}

			openList.Remove(curNode);
			closeList.Add(curNode);


			if (OnDrawTest != null) {
				OnDrawTest(step, DrawNodeType.CloseNode, curNode);
			}

			//查找周边节点并计算代价
			for (int m = -1; m < 2; m++) {
				for (int n = -1; n < 2; n++) {
					//排除自己和对角线节点
					if (pathDir == PathDir.FourDir) {
						if (Mathf.Abs(m) + Mathf.Abs(n) != 1) {
							continue;
						}
					} else if (pathDir == PathDir.EightDir) {
						//排除自己
						if (m == 0 && n == 0) {
							continue;
						}
					}

					int x = curNode.Index.X + m;
					int y = curNode.Index.Y + n;

					AStarNode node = AStarGridMgr.grid.GetNode(x, y);

					//如果不能行走，或已被排除，则Pass
					if (node == null || !node.CanMove || closeList.Contains(node)) {
						continue;
					}

					int gCost = curNode.GCost + AStarGridMgr.grid.CalcuteCost(curNode, node);
					if (!openList.Contains(node)) {
						//						 || gCost < node.GCost

						node.GCost = gCost;
						node.HCost = AStarGridMgr.grid.CalcuteCost(node, endNode);
						node.Parent = curNode;

						openList.Add(node);

						if (OnDrawTest != null) {
							OnDrawTest(step, DrawNodeType.OpenNode, node);
						}
					}
				}
			}
		}

		if (openList.Count <= 0) {
			isFindPath = true;
			if (OnFindPath != null) {
				OnFindPath(GetPath(startNode, curNode));
			}
			MyLog.LogError("Cann't Find Path");
		}
	}

	List<Vector3> nodeList = new List<Vector3>();
	private List<Vector3> GetPath(AStarNode start, AStarNode end)
	{
		if (end != null)
		{
			nodeList.Clear();

			//此处不能用endNode一直往上Parent,多次寻路会死循环
			while (end != start)
			{
				nodeList.Add(end.Position);
				end = end.Parent;
			}
		}

		return nodeList;
	}

	public override void onDestroy()
	{
		base.onDestroy();
		OnDrawTest -= AStarRes.DrawNode;
	}
}
